package util.http.client5;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.entity.mime.FileBody;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.entity.mime.StringBody;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import util.file.FileOperation;


public class HttpClient5PoolUtil {

	private static final Logger logger = LoggerFactory.getLogger(HttpClient5PoolUtil.class);
	
	public static final String UTF8 = "UTF-8";
	public static final String GBK = "GBK";
	public static final String GB2312 = "GB2312";
	
	public static final String UPLOAD_FILE = "UPLOAD_FILE";
	public static final String DOWNLOAD_FILE = "DOWNLOAD_FILE";
	public static final String  DEFAULT_UPLOAD_KEY = "file";
	
	protected HttpClient5Config httpClient5Config;
	protected HttpClient5Request httpClient5Request;
	protected HttpClient5Response httpClient5Response;
	
	public HttpClient5PoolUtil(HttpClient5Config httpClient5Config,HttpClient5Request httpClient5Request) {
		this.httpClient5Config = httpClient5Config;
		this.httpClient5Request = httpClient5Request;
		
		checkProperty();
	}
	
	public HttpClient5PoolUtil(HttpClient5Request httpClient5Request) {
		this.httpClient5Config = new HttpClient5Config();
		this.httpClient5Request = httpClient5Request;
		
		checkProperty();
	}
	
	public HttpClient5Response get() throws ClientProtocolException, IOException, ParseException {
		logger.info("Get Request Url : {}", httpClient5Request.getUrl());

		CloseableHttpClient httpclient = httpClient5Config.getCloseableHttpClient();

		HttpGet httpGet = new HttpGet(httpClient5Request.getUrl());

		httpGet.setConfig(httpClient5Config.getRequestConfig());

		Map<String, String> headers = httpClient5Request.getHeaders();
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpGet.addHeader(header.getKey(), header.getValue());
			}
		}
		
//		try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
//			HttpClient5Response responseBean = doResp(Method.GET.name(), response);
//
//			return responseBean;
//		}
		
		HttpClientResponseHandler<HttpClient5Response> httpClientResponseHandler = doRespHandler(Method.GET.name());

		return httpclient.execute(httpGet, httpClientResponseHandler);

	}
	

	public HttpClient5Response post() throws ClientProtocolException, IOException, ParseException {
		logger.info("Post Request Url : {}", httpClient5Request.getUrl());
		logger.info("Post Request ParamForm : {}", httpClient5Request.getParamForm());
		logger.info("Post Request ParamBody : {}", httpClient5Request.getParamBody());
		logger.debug("Post Request Headers : {}", httpClient5Request.getHeaders());
		logger.debug("Post Request Header : {}", httpClient5Request.getHeader());
		logger.debug("Post Request Charset : {}", httpClient5Request.getCharset());

		CloseableHttpClient httpclient = httpClient5Config.getCloseableHttpClient();

		HttpPost httpPost = new HttpPost(httpClient5Request.getUrl());

		httpPost.setConfig(httpClient5Config.getRequestConfig());

		boolean isForm = false;

		Map<String, String> headers = httpClient5Request.getHeaders();
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPost.addHeader(header.getKey(), header.getValue());
				isForm = ContentType.APPLICATION_FORM_URLENCODED.getMimeType().equalsIgnoreCase(header.getValue());
			}
		}

		Map<String, String> paramForm = httpClient5Request.getParamForm();
		Charset charset = Charset.forName(httpClient5Request.getCharset());
		if (paramForm != null || isForm) {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			if (paramForm != null) {
				for (Entry<String, String> form : paramForm.entrySet()) {
					nvps.add(new BasicNameValuePair(form.getKey(), form.getValue()));
				}
			}

			httpPost.setEntity(new UrlEncodedFormEntity((Iterable<? extends NameValuePair>) nvps, charset));
		} else {
			httpPost.setEntity(new StringEntity(httpClient5Request.getParamBody(), charset));
		}

//		try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
//
//			HttpClient5Response responseBean = doResp(Method.POST.name(), response);
//
//			return responseBean;
//
//		}
		
		HttpClientResponseHandler<HttpClient5Response> httpClientResponseHandler = doRespHandler(Method.POST.name());

		return httpclient.execute(httpPost, httpClientResponseHandler);

	}
	
	public HttpClient5Response put() throws ClientProtocolException, IOException, ParseException {
		logger.info("Put Request Url : {}", httpClient5Request.getUrl());
		logger.info("Put Request ParamForm : {}", httpClient5Request.getParamForm());
		logger.info("Put Request ParamBody : {}", httpClient5Request.getParamBody());
		logger.debug("Put Request Headers : {}", httpClient5Request.getHeaders());
		logger.debug("Put Request Header : {}", httpClient5Request.getHeader());
		logger.debug("Put Request Charset : {}", httpClient5Request.getCharset());

		CloseableHttpClient httpclient = httpClient5Config.getCloseableHttpClient();

		HttpPut httpPut = new HttpPut(httpClient5Request.getUrl());

		httpPut.setConfig(httpClient5Config.getRequestConfig());

		boolean isForm = false;

		Map<String, String> headers = httpClient5Request.getHeaders();
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPut.addHeader(header.getKey(), header.getValue());
				isForm = ContentType.APPLICATION_FORM_URLENCODED.getMimeType().equalsIgnoreCase(header.getValue());
			}
		}

		Map<String, String> paramForm = httpClient5Request.getParamForm();
		Charset charset = Charset.forName(httpClient5Request.getCharset());
		if (paramForm != null || isForm) {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			if (paramForm != null) {
				for (Entry<String, String> form : paramForm.entrySet()) {
					nvps.add(new BasicNameValuePair(form.getKey(), form.getValue()));
				}
			}

			httpPut.setEntity(new UrlEncodedFormEntity((Iterable<? extends NameValuePair>) nvps, charset));
		} else {
			httpPut.setEntity(new StringEntity(httpClient5Request.getParamBody(), charset));
		}

//		try (CloseableHttpResponse response = httpclient.execute(httpPut)) {
//
//			HttpClient5Response responseBean = doResp(Method.PUT.name(), response);
//
//			return responseBean;
//
//		}
		
		HttpClientResponseHandler<HttpClient5Response> httpClientResponseHandler = doRespHandler(Method.PUT.name());

		return httpclient.execute(httpPut, httpClientResponseHandler);

	}
	
	
	public HttpClient5Response downloadFile() throws ClientProtocolException, IOException, ParseException {
		logger.info("DownloadFile Request Url : {}", httpClient5Request.getUrl());

		CloseableHttpClient httpclient = httpClient5Config.getCloseableHttpClient();

		HttpGet httpGet = new HttpGet(httpClient5Request.getUrl());

		httpGet.setConfig(httpClient5Config.getRequestConfig());

		Map<String, String> headers = httpClient5Request.getHeaders();
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpGet.addHeader(header.getKey(), header.getValue());
			}
		}

//		try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
//			HttpClient5Response responseBean = doResp(DOWNLOAD_FILE, response);
//
//			return responseBean;
//		}
		
		HttpClientResponseHandler<HttpClient5Response> httpClientResponseHandler = doRespHandler(DOWNLOAD_FILE);

		return httpclient.execute(httpGet, httpClientResponseHandler);

	}
	
	public HttpClient5Response uploadFile() throws ClientProtocolException, IOException, ParseException {
		logger.info("UploadFile Request Url : {}", httpClient5Request.getUrl());
		logger.info("UploadFile Request ParamForm : {}", httpClient5Request.getParamForm());
		logger.info("UploadFile Request PathName : {}", httpClient5Request.getPathName());
		logger.debug("UploadFile Request Headers : {}", httpClient5Request.getHeaders());

		CloseableHttpClient httpclient = httpClient5Config.getCloseableHttpClient();

		HttpPost httpPost = new HttpPost(httpClient5Request.getUrl());

		httpPost.setConfig(httpClient5Config.getRequestConfig());

		Map<String, String> headers = httpClient5Request.getHeaders();
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPost.addHeader(header.getKey(), header.getValue());
			}
		}

		FileBody fileBody = new FileBody(new File(httpClient5Request.getPathName()));
		MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create()
				.addPart(httpClient5Request.getUploadKey(), fileBody);

		for (Entry<String, String> val : httpClient5Request.getParamForm().entrySet()) {
			StringBody eachVal = new StringBody(val.getValue(),
					ContentType.create("text/plain", Charset.forName(httpClient5Request.getCharset())));
			multipartEntityBuilder.addPart(val.getKey(), eachVal);
		}

		HttpEntity reqEntity = multipartEntityBuilder.build();
		httpPost.setEntity(reqEntity);

//		try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
//
//			HttpClient5Response responseBean = doResp(UPLOAD_FILE, response);
//
//			return responseBean;
//		}
		
		HttpClientResponseHandler<HttpClient5Response> httpClientResponseHandler = doRespHandler(UPLOAD_FILE);

		return httpclient.execute(httpPost, httpClientResponseHandler);

	}

	
	@Deprecated
	private HttpClient5Response doResp(String type, CloseableHttpResponse response) throws IOException, ParseException {

		logger.info("Request type : {} --- DoResp Response Code : {}",type, response.getCode());

		HttpEntity entity = response.getEntity();

		if (type.equals(DOWNLOAD_FILE)) {

			InputStream inputStream = entity.getContent();
			saveFile(inputStream, httpClient5Request.getPathName());
			EntityUtils.consume(entity);

			return null;
		} else {
			String respStr = EntityUtils.toString(entity, Charset.forName(httpClient5Request.getCharset()));
			logger.info("Request type : {} --- DoResp Response Entity : {}",type, respStr);

			EntityUtils.consume(entity);

			return buildResp(response, respStr);
		}
	}
	
	@Deprecated
	private HttpClient5Response buildResp(CloseableHttpResponse response, String respStr) {
		HttpClient5Response responseBean = new HttpClient5Response();
		responseBean.setContent(respStr);
		responseBean.setHttpStatusCode(response.getCode());

		HashMap<String, String> respHeader = new HashMap<String, String>();
		int index = 1;

		for (Header header : response.getHeaders()) {
			String name = header.getName();
			String value = header.getValue();

			logger.info("Header Name : {} --- Value : {}", name, value);

			if (name == null || "".equals(name)) {
				if (value != null && !"".equals(value)) {
					respHeader.put("header" + index, header.getValue());
				}
			} else {
				respHeader.put(header.getName(), header.getValue());
				if (name.equals("Set-Cookie")) {
					responseBean.setCookies(value);
				}
			}

		}

		responseBean.setHeaders(respHeader);

		return responseBean;
	}
	
	private HttpClientResponseHandler<HttpClient5Response> doRespHandler(String type) {
		
		logger.info("Request type : {} ",type);
		
		HttpClientResponseHandler<HttpClient5Response> responseHandler = response -> {
			
			logger.info("DoRespHandler Response Code : {}",response.getCode());
			
			HttpEntity entity = response.getEntity();

			if (type.equals(DOWNLOAD_FILE)) {

				InputStream inputStream = entity.getContent();
				saveFile(inputStream, httpClient5Request.getPathName());
				EntityUtils.consume(entity);

				return null;
			} else {
				String respStr = EntityUtils.toString(entity, Charset.forName(httpClient5Request.getCharset()));
				logger.info("Request type : {} --- DoRespHandler Response Entity : {}",type, respStr);

				EntityUtils.consume(entity);

				return buildRespHandler(response, respStr);
			}
			
		};

		return responseHandler;
	}
	
	
	private HttpClient5Response buildRespHandler(ClassicHttpResponse response, String respStr) {
		HttpClient5Response responseBean = new HttpClient5Response();
		responseBean.setContent(respStr);
		responseBean.setHttpStatusCode(response.getCode());

		HashMap<String, String> respHeader = new HashMap<String, String>();
		int index = 1;

		for (Header header : response.getHeaders()) {
			String name = header.getName();
			String value = header.getValue();

			logger.info("Header Name : {} --- Value : {}", name, value);

			if (name == null || "".equals(name)) {
				if (value != null && !"".equals(value)) {
					respHeader.put("header" + index, header.getValue());
				}
			} else {
				respHeader.put(header.getName(), header.getValue());
				if (name.equals("Set-Cookie")) {
					responseBean.setCookies(value);
				}
			}

		}

		responseBean.setHeaders(respHeader);

		return responseBean;
	}
	
	
	private void saveFile(InputStream inputStream, String pathName) {
		FileOperation fileOperation = new FileOperation(pathName);

		try {
			fileOperation.createBinaryFile(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
			logger.error("pathName", pathName);
		}
	}
	
	private void checkProperty() {
		if (httpClient5Request != null) {
			if (httpClient5Request.getCharset() == null) {
				httpClient5Request.setCharset(UTF8);
			}
			if(httpClient5Request.getUploadKey() == null) {
				httpClient5Request.setUploadKey(DEFAULT_UPLOAD_KEY);
			}
		}
	}
}
